﻿(*   Copyright 2011 Alexander Omelchuk
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *)

namespace Lisp
open Eval
open System.Linq.Expressions

type NativeFunctions =
  static member CAR (expr:SExpr) =
    match expr with
    | Lst(car,_) -> car
    | x -> raiseExprError isNotList x

  static member CDR (expr:SExpr) =
    match expr with
    | Lst(_,cdr) -> cdr
    | x -> raiseExprError isNotList x
  
  static member CONS (expr1:SExpr) (expr2:SExpr) = Cons(expr1, expr2) :> SExpr

  static member LIST (exprs:SExpr) = exprs

  static member ATOM (expr:SExpr) =
    match expr with
    | Lst _ -> Nil
    | _ -> T

  static member EVAL (dEnv:IDynamicEnvironment) (expr:SExpr) =
    doInLexical (new LEnvironment(None, None))
                (fun () -> (compileOne expr).Compile().DynamicInvoke(dEnv))

  static member ``+`` (expr1:SExpr) (expr2:SExpr) =
    Lisp.Dynamic.ApplyBinaryOp(ExpressionType.Add, expr1, expr2)

  static member ``-`` (expr1:SExpr) (expr2:SExpr) =
    Lisp.Dynamic.ApplyBinaryOp(ExpressionType.Subtract, expr1, expr2)

  static member ``*`` (expr1:SExpr) (expr2:SExpr) =
    Lisp.Dynamic.ApplyBinaryOp(ExpressionType.Multiply, expr1, expr2)

  static member ``/`` (expr1:SExpr) (expr2:SExpr) =
    Lisp.Dynamic.ApplyBinaryOp(ExpressionType.Divide, expr1, expr2)

  static member ``=`` (expr1:SExpr) (expr2:SExpr) =
    if Lisp.Dynamic.ApplyBinaryOp(ExpressionType.Equal, expr1, expr2) :?> bool
    then T else Nil

  static member ``/=`` (expr1:SExpr) (expr2:SExpr) =
    if Lisp.Dynamic.ApplyBinaryOp(ExpressionType.NotEqual, expr1, expr2) :?> bool
    then T else Nil

  static member EQ (expr1:SExpr) (expr2:SExpr) =
    if expr1 = expr2 then T else Nil

//  member eq = function
//    | car::cdr::Nil -> if eq(car, cdr) then T else Nil
//    | _::Nil -> raiseArgError tooFewParamsFun
//    | _::_   -> raiseArgError tooManyParamsFun
//    | x      -> raiseExprError isNotList x